function mlhMain() {

    //список загруженных шаблонов
    this.templates = [];

    //полушение шаблона
    this.getTemplate = function(callback) {
    	var file = app.path.template;
        var this_ = this;
        if ($.inArray(file, this.templates) < 0) {
            $.ajax(
                {
                    url: file,
                    dataType: 'text',
                    success: function (templateBody) {
                        this_.templates.push(file);
                        try {
                            $(templateBody.split('<!-- TEMPLATE ')).each(function() {
                                var templateName = this.split('-->')[0];
                                var templateText = this.substr(templateName.length + 3);
                                $.template($.trim(templateName), $.trim(templateText));
                            });
                            if (app.debug.template) console.log("Loaded " + file);
                            var success = true;
                        } catch (e) {
                            if (app.debug.template)console.log("Can't parse " + file + "\n" + e.message);
                            success = false;
                        }
                        if (typeof(callback) === 'function') {
                            callback(success);
                        }
                    },
                    error: function() {
                        if (app.debug.template) console.log("Can't load " + file);
                        callback(false);
                    }
                }
            );
        } else {
            if (app.debug.template) console.log("Get template " + file + " from cache");
            callback(true);
        }
    }
    //загрузка скрипта
    this.loadScript = function(name){
        var file = app.path.scripts + name + '.js';
        var this_ = this;
        var result = false;
        $.ajax(
            {
                url: file,
                async: false,
                dataType: 'script',
                success: function (templateBody) {
                    if (app.debug.script) console.log("Loaded script " + name);
                    result = true;
                },
                error: function() {
                    if (app.debug.script) console.log("Can't load script " + name);
                    result = false;
                }
            }
        );
        return result;
    }
    this.loadTemplate = function(){
        this.getTemplate(function(){
            app.main.fire('on_template_loaded');
        });
    }
    //Указания if-data
    this.ifData = function(name, value){
    	if(value) {
    		$("*[if-data*=" + name + "]").show();
    		$("*[if-no-data*=" + name + "]").hide();
    	}else{
    		$("*[if-data*=" + name + "]").hide();
    		$("*[if-no-data*=" + name + "]").show();
    	}
    }
    //список подписанных событий
    var observers = new Array;

    //подписаться на событие
    this.bind = function(event, obj, caller, one) {
        var fullEventName = 'event_' + event;
        if (observers[fullEventName] == undefined) {
            observers[fullEventName] = new Array;
        }
        if (observers[fullEventName][caller] == undefined) {
            observers[fullEventName][caller] = new Array();
        }
        observers[fullEventName][caller].push(
        {
            obj:obj,
            once: one ? true : false
        });
        if (app.debug.bind) console.log('Bind event: ', event, caller, one ? 'ONCE' : '');
    }

    //выполнить ивент
    this.fire = function(event, val) {
        var fullEventName = 'event_' + event;
        if (app.debug.fire) console.log('Fire event: ', event);
        if (observers[fullEventName] == undefined) {
            return false;
        }
        var caller;
        for (caller in observers[fullEventName]) {
            var iLen = observers[fullEventName][caller].length;
            for (var i = 0; i < iLen; i++) {
                if (typeof(observers[fullEventName][caller][i]) == 'object' && typeof(observers[fullEventName][caller][i].obj) == 'function') {
                    try {
                        observers[fullEventName][caller][i].obj(val);
                        if (observers[fullEventName][caller][i].once) {
                            delete observers[fullEventName][caller][i];
                            if (app.debug.observed) console.log('Event ONCE: ', event, caller);
                        } else {
                            if (app.debug.observed) console.log('Event: ', event, caller);
                        }
                    } catch (err) {
                        if (app.debug.observer_error) {
                            console.log('OBSERVER ERROR: ' + err.message);
                            console.log('OBSERVER ERROR DETAILS:');
                            console.log('EVENT NAME: ' + fullEventName);
                            console.log('EVENT CALLER:' + caller);
                            console.log('FUNCTION:');
                            console.log(observers[fullEventName][caller][i].obj);
                        }
                    }
                }
            }
        }
        return true;
    }

    //отписаться от события
    this.unbind = function(event, caller) {
        var fullEventName = 'event_' + event;
        if (observers[fullEventName] != undefined) {
            if (caller == undefined) {
                delete observers[fullEventName];
            } else {
                if (observers[fullEventName][caller] != undefined) {
                    delete observers[fullEventName][caller];
                }
            }
        }
    }

    //отписать от всех событий по ключу
    this.unbindAll = function(caller) {
        for (var fullEventName in observers) {
            if (observers[fullEventName][caller]) {
                delete observers[fullEventName][caller];
            }
        }
    }
    this.urlChecker = function(){
        if(location.hash && location.hash != '#' + app.currentUrl.join('/')){
            this.ajaxObserver(location.hash);
        }
    }
    this.ajaxObserver = function(data){
    	if(typeof data == 'string') data = {href:data};
        if(!data.href) return false;
        if(data.href.substr(0,1) == '#') data.href = data.href.substr(1);
        var url = data.href.split("/");
        if(!url[0])url.shift();
        var model = data['rel']|| url[0];
        if(model=='help' || model == 'imageUploader')data['noChangeHash']=true;
        if(model == 'imageUploader')data['noChangeCurrentUrl']=true;
        if(!data['noChangeHash'])location.hash = data.href;
        switch(model){
            case "main" :{
                return this.ajaxObserver(app.default_address);
            }
                break;
            case "news": {
                model = 'article';
                url = ['article','news'];
            }                
                break;

            case "register":{
                model = 'user';
                url = ['user','edit'];
            }                
                break;
            case "registered_only":
            case "ask_register_shop":
            case "404":{
                model = 'static';
            }
                break;
            /*case "imageUploader":{
                model = 'help';
            }*/
                break;
        }
        if(model != 'static') this.mask();
        if(!data['noChangeCurrentUrl'])app.currentUrl = url;
        if(!app[model]){
            if(!app.main.loadScript(model)){
                app.main.unmask();
                this.ajaxObserver(app.page_404);
                return false;
            }
        }
        if(typeof url[1] != 'undefined'){
            if(typeof app[model][url[1]] == 'function'){
                app[model][url[1]](url);
                return true;
            }
        }
        if(typeof app[model]['init'] == 'function'){
            app[model].init(url);
            return true;
        }
        app.main.unmask();
        return false;
    }
    
    this.goByAdress = function(){
    	var address = location.href.replace(location.origin, '');
    	if(address.indexOf('#')>-1){
    		var adr = address.split('#');
    		/*if(adr[1].length>1){
    			location.href = location.origin + adr[1];
    			return false;
    		}*/
    		address = adr[1];
    	}
    	if(!address || address=='/') address = app.default_address;
        var noChangeCurrentUrl = false;
        if(address.indexOf('help/')>-1) {
            app.currentUrl = address.split('/');
            address = address.replace('help/','article/');
            noChangeCurrentUrl = true;
        }
        if(address.indexOf('imageUploader')>-1) {
            noChangeCurrentUrl = true;
        }
    	return this.ajaxObserver({
    		href: address,
    		noChangeHash: true,
            noChangeCurrentUrl: noChangeCurrentUrl
    	});
    }
    
    this.mask = function(){
    	$("body:first").mask('Загрузка',100);
    }
    this.unmask = function(){
    	var element = $("body:first");
        if (element.data("_mask_timeout") !== undefined) {
            clearTimeout(element.data("_mask_timeout"));
            element.removeData("_mask_timeout");
        }
        element.unmask();
    }
    this.reloadUserInfo = function(callback){
        mlhAPI.main.info({
            callback:function(data){
                app['user_info'] = data.data.user_info;
                if(callback)callback();
            }
        })
    }
    this.getUserInfo = function(){
    	this.reloadUserInfo(function(){
            app.main.fire('on_update_user_info', app['user_info']);
        });
    }
}

function extendAPI(){
    if(mlhAPI.article['getListByUser']) return;
    //список артиклей пользователя
    mlhAPI.article.getListByUser = function(data){
        if(!data['user_id']) return false;
        if(!data['pagination'])data['asTable']=1;
        jQuery.extend(data, {
            v: 'showByUser'
        });
        this.actions(data);
        return true;
    }
    //список артиклей конкретного магазина
    mlhAPI.article.getListByShop = function(data){
        if(!data['shop']) return false;
        if(!data['pagination'])data['asTable']=1;
        jQuery.extend(data, {
            v: 'showByShop'
        });
        this.actions(data);
        return true;
    }
    //путь по категориям
    mlhAPI.category.road = function(data){
        if(!data['id']) return false;
        jQuery.extend(data, {
            v: 'backStep'
        });
        this.actions(data);
        return true;
    }
    //список шаблонов
    mlhAPI.main.getTemplateList = function(data){
        mlhAPI.get({
            v: 'show',
            c: 'template'
        },data.callback);
        return true;
    }
}
var app = {
    started: false,
    push: function(callback, params) {
        if (this.started) {
            callback(params);
            return;
        } else {
            app.main.bind('start', callback, "system");
        }
    },
    go: function(url){
        this.main.ajaxObserver(url);
    },
    start: function() {
        app.started = true;
        extendAPI();
        app.main.bind('on_template_loaded', function(){
            app.main.bind('on_update_user_info', function(info){
                app.main.fire('start', 'system');
                app.topMenu.init();
                if(!app.main.goByAdress() && app.start_load){
                    if(typeof app.start_load == 'string'){
                        app.start_load = {
                            href:app.start_load,
                            noChangeHash: true
                        };
                    } else {
                        app.start_load['noChangeHash'] = true;
                    }
                    app.main.ajaxObserver(app.start_load);
                }
                setInterval(function(){
                    app.main.urlChecker();
                },500);
            },'system', true);
            app.main.getUserInfo();
        }, "system", true);
        app.main.loadTemplate();
    },
    debug: {
        fire: false,
        observed: true,
        observer_error: true,
        bind: false,
        ajax: false,
        template: false,
        script: true
    },
    start_load: false,
    elements:{},
    currentUrl: [],
    path:{
    	template: '/view/templates/template.html',
        scripts: '/js/'
    },
    default_address: '#article/1',
    page_404: '#404',
    texts:{
    	shop: ["Магазин","Магазины"],
    	article: ["Статья","Статьи"],
    	news: ["Новость","Новости"],
    	product: ["Продукт","Продукты"]
    },
    //копируем массив
    cloneArray: function(arr){
        return arr.join('<*>').split('<*>');
    },
    decorate:{
    	//строим select списки
        buildSelectIttems: function(element, settings){
            settings = jQuery.extend({
                list: {},
	            key: 'id',
                title: 'title',
                active: 0,
                multilevelKey: false,
                levelDelimiter: "- ",
                level: 0,
                spaceAdd: '&nbsp;&nbsp;',
                baseTemplate: function(text){return '<option>' + text + '</option>'},
                manipulate: false
	        }, settings);
            var add = '';
            if(settings.level){
                add+=settings.levelDelimiter;
                for(var m=0; m<settings.level; m++) add=settings.spaceAdd+add;
            }
            for(var k in settings.list){
                var text = typeof settings.title == 'string' ? settings.list[k][settings.title] : settings.title(settings.list[k]);
                var el = $(settings.baseTemplate(add+text)).attr('value', settings.list[k][settings.key]);
                if(typeof settings.manipulate == 'function') el = settings.manipulate(el);
                if(settings.list[k][settings.key] == settings.active) el.attr('selected','selected');
                element.append(el);
                if(settings.multilevelKey && settings.list[k][settings.multilevelKey]) {
                    var newSettings = jQuery.extend({},settings);
                    app.decorate.buildSelectIttems(element, jQuery.extend(newSettings,{
                        level: newSettings.level+1,
                        list: settings.list[k][settings.multilevelKey]
                    }));
                }
            }
            if(settings.level == 0) element.val(settings.active);
        },
        //валидация полей
        validateRule: function(elements,params){
        	params = $.extend({
		        error : 'error',
		        success : 'success',
		        reqText : 'Это поле обязательно надо заполнить'
		    },params);
        	var timer=false;
            $(elements).change(function(){
                var controlGroup = $(this).parents('.control-group:first');
                controlGroup.attr('class','row control-group span12 no_left_margin');
                controlGroup.find('.alert:first').hide();
                var el = $(this);
                var value = el.val();
                if(this.tagName=='SELECT' && (value==0 || value == '0') )value = false;
                if(value){
                    var error = false;
                    var name = el.attr('name');
                    if(params['rules'] && params['rules'][name]){
                        error = params['rules'][name](value,controlGroup);
                    }
                }else if($(this).attr('rel')=='req'){
                    error = params.reqText;
                }
				if(error) controlGroup.find('.alert:first').text(error).show();
                controlGroup.addClass(error ? params.error : params.success);
                params['endValid'](error);
            })
            .keyup(function(){
            	if(timer){
            		clearTimeout(timer);
            		timer = false;
            	}
            	var el = $(this);
            	timer = setTimeout(function(){
            		el.change();
            	}, 1500);
            });
        },
        //устанавливаем евенты
        setEvents: function(base){
            base = base || app.elements['general_content'];
            $(base).find('.help, .tooltip').css({display: 'block'}).tooltip({
                delay: { show: 300, hide: 200 },
            });
            $(base).find('.explain').popover(
            {
                delay: { show: 1000, hide: 200 }
            });
        },
        //форма для загрузки картинок
        imageForm: function(element, image, newImageFlag, count, options){
            count = count || 1;
            element.find('.btn[rel=setImage]').click(function(){
                var img = typeof image == 'function' ? image() : image;
                app.static[count == 1 ? "imageUploaderSingle" : "imageUploaderMultiple"]($.extend({
                    autoUpload: true,
                    formElement: element,                    
                    list: img ? (img['length'] ? img : [img]) : false
                }, options || {}), count, newImageFlag);
            });
        },
        imageGallery: function(element,list,first,options){
            app.static.imageGallery(element,list,first,options);
        }
    }
};

(function($) {
    app['main'] = new mlhMain();
    app.main.bind('mlhAPI_loaded', extendAPI, "system");
})(jQuery);

$(document).ready(app.start);